﻿/*
 * Compression
 * 
 * original Copyright (c) 2002, 2003 Marco Schmidt.
 * All rights reserved.
 * 
 * C# translation and update 
 * Copyright (c) Adam Sida, asida@centrum.cz
 * 
 * All rights reserved.
 */
using System;
using System.Collections;
//using System.Drawing.Imaging;
using System.Text;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
//using Petra.Gis.Imaging;
using Petra.Gis.ImagingServices.Tiff;



namespace Petra.Gis.ImagingServices.Tiff
{

  /// <summary>
  /// The abstract base class for a TIFF decoder, a class that decompresses one block (tile or
  /// strip) of image data and understands one compression type.
  /// Each child class implements the decoding of a particular TIFF compression type
  /// in its {@link #decode} method.
  /// Each child must implement all functionality for decode band into destination bitmap
  /// </summary>
  public abstract class Compression
  {


    //protected FileStream _tiffStream;
    //protected TiffReader _tiffReader;
    //protected TiffBitmapDecoder _codec;
    //protected Block _currBlock;
    
    protected byte _bitsPerPixel;
    protected PixelFormat _blockPixelFormat;
    protected PixelFormat _outPixelFormat;

    protected byte[] _uncompressedDataBuff;

    //this is for state when decoder is called to get its compression capability
    //empty constructor is used and decoder is invalid
    //private bool _isDecoderValid = false;


    #region constructors


    public Compression()
    { 
    }


    ///// <summary>
    ///// default constructor that initializes all basic.
    ///// must be called by inherited objects
    ///// </summary>
    ///// <param name="image"></param>
    //public Compression(BlockBitmapSource tiffBmp)
    //{
    //  if (tiffBmp == null)
    //    throw new ArgumentException("No Image object was given to this decoder.");

    //  //_image = imageFrame;
    //  //_codec = imageFrame.Codec;

    //  //_tiffReader = _codec.TiffReader;

    //  //_isDecoderValid = true;
    //}


    #endregion constructors



    #region public members


    //protected TiffBitmapPage _image;


    //public TiffBitmapPage BitmapFrame
    //{
    //  get { return _image; }
    //  set { _image = value; }
    //}


    ///<summary>
    /// Decode data from input and write the decompressed pixel data to
    /// the image associated with this decoder.
    /// Child classes must override this method to implement the decoding
    /// for a particular compression type.
    ///</summary>
    public abstract byte[] DecompressBlock(BitmapBlock block, byte[] comprData, int srcBitsPerSample); //decode whole image block
    //public abstract byte[] Decompress(Int32Rect bounds, byte[] inputData, int bitsPerSample, startColumn, endColumn); //decode whole image block
    //public abstract void decode(Bitmap destBitmap); //decode whole image directly into existing bitmap



    
    /// <summary>
    /// Returns an array with values of all compression types supported by
    /// this decoder (see the CompressionType enumerators
    /// Normally, this is only one value, but some compression types got assigned more than one constant
    /// and also, a decoder could be capable of dealing with more than one type of compression
    /// if the compression types are similar enough to justify that
    /// </summary>
    public abstract CompressionType[] CompressionTypes {get; }

    #endregion public members


    #region private members

    protected byte[] alignDataToStride(int paddingWidth, int paddingHeight, byte[] rawDecompressedData, int dataBitsPerSample)
    {

      if (BitmapBlock.GetStrideLenght(paddingWidth, dataBitsPerSample) == (paddingWidth * (dataBitsPerSample / 8)))
        return _uncompressedDataBuff;

      int srcIndex = 0;
      int destIndex = 0;
      int stride = BitmapBlock.GetStrideLenght(paddingWidth, dataBitsPerSample);
      int pixelSize = dataBitsPerSample / 8;
      int rowDataByteLenght = paddingWidth * pixelSize;
      int rowReminder = stride - rowDataByteLenght;
      byte[] result = new byte[stride * paddingHeight];

      for (int i = 0; i < paddingHeight; i++)
      {
        Array.Copy(rawDecompressedData, srcIndex, result, destIndex, rowDataByteLenght);
        srcIndex += rowDataByteLenght;
        destIndex += stride;
      }

      return result;
    }




    //private void storeRow(byte[] data, int offset)
    //{
    //  //codec.setProgress(processedTileRows++, totalTileRows);
    //  // get current row number and increase field currentRow by one
    //  int y = currentRow++;
    //  // buffer index field is reset to zero so that putBytes will start at the beginning of the buffer next time
    //  bufferIndex = 0;
    //  // leave if we don't need that row because of bounds
    //  //if (!_codec.isRowRequired(y))
    //  //{
    //  //  return;
    //  //}
    //  // adjust y so that it will be in bounds coordinate space
    //  y -= _codec.BoundsY1;
    //  // get leftmost and rightmost pixel index of the current tile
    //  int x1 = getX1();
    //  int x2 = getX2();
    //  // compute number of pixels, adjust for bounds
    //  int numPixels = x2 - x1 + 1;
    //  int leftPixels = 0;
    //  if (getX1() < _codec.BoundsX1)
    //  {
    //    leftPixels = _codec.BoundsX1 - getX1();
    //  }
    //  int rightPixels = 0;
    //  if (getX2() > _codec.BoundsX2)
    //  {
    //    rightPixels = getX2() - _codec.BoundsX2;
    //  }
    //  numPixels -= (rightPixels + leftPixels);
    //  switch (_image.Ifd.ImageType)
    //  {
    //    case (ImageType.BILEVEL_BYTE):
    //      {
    //        IBilevelImage image = (IBilevelImage)_image;
    //        int index = offset + leftPixels;
    //        int x = getX1() - _codec.BoundsX1 + leftPixels;
    //        while (numPixels-- > 0)
    //        {
    //          if (data[index++] == (byte)MemoryBilevelImage.BiColorType.BLACK)
    //          {
    //            image.putBlack(x++, y);
    //          }
    //          else
    //          {
    //            image.putWhite(x++, y);
    //          }
    //        }
    //        break;
    //      }
    //    case (ImageType.BILEVEL_PACKED):
    //      {
    //        IBilevelImage image = (IBilevelImage)_image;
    //        int x = getX1() - _codec.BoundsX1 + leftPixels;
    //        image.putPackedBytes(x, y, numPixels, data, offset + (leftPixels / 8), leftPixels % 8);
    //        break;
    //      }
    //    case (ImageType.GRAY4):
    //      {
    //        IByteChannelImage image = (IByteChannelImage)_image;
    //        byte[] dest = new byte[data.Length * 2];
    //        ArrayConverter.decodePacked4Bit(data, 0, dest, 0, data.Length);
    //        for (int i = 0; i < dest.Length; i++)
    //        {
    //          int value = dest[i] & 15;
    //          value = (value << 4) | value;
    //          dest[i] = (byte)value;
    //        }
    //        image.putByteSamples(0, getX1() - _codec.BoundsX1 + leftPixels, y, numPixels, 1, dest, offset + leftPixels);
    //        break;
    //      }
    //    case (ImageType.PALETTED4):
    //      {
    //        IByteChannelImage image = (IByteChannelImage)_image;
    //        byte[] dest = new byte[data.Length * 2];
    //        ArrayConverter.decodePacked4Bit(data, 0, dest, 0, data.Length);
    //        image.putByteSamples(0, getX1() - _codec.BoundsX1 + leftPixels, y, numPixels, 1, dest, offset + leftPixels);
    //        break;
    //      }
    //    case (ImageType.GRAY8):
    //    case (ImageType.PALETTED8):
    //      {
    //        IByteChannelImage image = (IByteChannelImage)_image;
    //        image.putByteSamples(0, getX1() - _codec.BoundsX1 + leftPixels, y, numPixels, 1, data, offset + leftPixels);
    //        break;
    //      }
    //    case (ImageType.CMYK32_INTERLEAVED):
    //      {
    //        IByteChannelImage image = (IByteChannelImage)_image;
    //        byte[] dest = new byte[data.Length];
    //        int numSamples = _currBlock.Width;
    //        CMYKConversion.convertCMYK32InterleavedToRGB24Planar(
    //          data, 0,
    //          dest, 0,
    //          dest, numSamples,
    //          dest, numSamples * 2,
    //          numSamples);
    //        image.putByteSamples(RGBIndex.Red, getX1() - _codec.BoundsX1 + leftPixels, y, numPixels, 1, dest, leftPixels);
    //        image.putByteSamples(RGBIndex.Green, getX1() - _codec.BoundsX1 + leftPixels, y, numPixels, 1, dest, numSamples + leftPixels);
    //        image.putByteSamples(RGBIndex.Blue, getX1() - _codec.BoundsX1 + leftPixels, y, numPixels, 1, dest, 2 * numSamples + leftPixels);
    //        break;
    //      }
    //    /*			case(TIFFImageFileDirectory.TYPE_CMYK32_PLANAR):
    //          {
    //            IByteChannelImage image = (IByteChannelImage)codec.getImage();
    //            byte[] dest = new byte[data.length];
    //            int numSamples = ifd.getTileWidth();
    //            CMYKConversion.convertCMYK32PlanarToRGB24Planar(
    //              data, 0,
    //              data, numPixels,
    //              data, numPixels * 2,
    //              data, numPixels * 3,
    //              dest, 0,
    //              dest, numSamples,
    //              dest, numSamples * 2,
    //              numSamples);
    //            image.putByteSamples(RGBIndex.Red, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, leftPixels);
    //            image.putByteSamples(RGBIndex.Green, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, numSamples + leftPixels);
    //            image.putByteSamples(RGBIndex.Blue, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, 2 * numSamples + leftPixels);
    //            break;
    //          }*/
    //    case (ImageType.RGB24_INTERLEAVED):
    //      {
    //        IByteChannelImage image = (IByteChannelImage)_image;
    //        offset += leftPixels * 3;
    //        for (int i = 0, x = getX1() - _codec.BoundsX1 + leftPixels; i < numPixels; i++, x++)
    //        {
    //          image.putByteSample(RGBIndex.Red, x, y, data[offset++]);
    //          image.putByteSample(RGBIndex.Green, x, y, data[offset++]);
    //          image.putByteSample(RGBIndex.Blue, x, y, data[offset++]);
    //        }
    //        break;
    //      }
    //    case (ImageType.RGB48_INTERLEAVED):
    //      {
    //        IShortChannelImage image = (IShortChannelImage)_image;
    //        offset += leftPixels * 3;
    //        short[] triplet = new short[3];
    //        //bool littleEndian = codec.getByteOrder() == TIFFCodec.BYTE_ORDER_INTEL;
    //        for (int i = 0, x = getX1() - _codec.BoundsX1 + leftPixels; i < numPixels; i++, x++)
    //        {
    //          for (int j = 0; j < 3; j++, offset += 2)
    //          {
    //            if (_codec.Endian == TiffDataEndian.LittleIndian)
    //            {
    //              triplet[j] = ArrayConverter.getShortLE(data, offset);
    //            }
    //            else
    //            {
    //              triplet[j] = ArrayConverter.getShortBE(data, offset);
    //            }
    //          }
    //          image.putShortSample(RGBIndex.Red, x, y, triplet[0]);
    //          image.putShortSample(RGBIndex.Green, x, y, triplet[1]);
    //          image.putShortSample(RGBIndex.Blue, x, y, triplet[2]);
    //        }
    //        break;
    //      }
    //    case (ImageType.LOGLUV32_INTERLEAVED):
    //      {
    //        if (_image.Compression == CompressionType.SGILOG)
    //        {
    //          IByteChannelImage image = (IByteChannelImage)_image;
    //          int numSamples = _currBlock.Width;
    //          byte[] red = new byte[numSamples];
    //          byte[] green = new byte[numSamples];
    //          byte[] blue = new byte[numSamples];
    //          LogLuvConversion.convertLogLuv32InterleavedtoRGB24Planar(data, red, green, blue, numSamples);
    //          image.putByteSamples(RGBIndex.Red, getX1() - _codec.BoundsX1 + leftPixels, y, numPixels, 1, red, leftPixels);
    //          image.putByteSamples(RGBIndex.Green, getX1() - _codec.BoundsX1 + leftPixels, y, numPixels, 1, green, leftPixels);
    //          image.putByteSamples(RGBIndex.Blue, getX1() - _codec.BoundsX1 + leftPixels, y, numPixels, 1, blue, leftPixels);
    //        }
    //        else
    //          if (_image.Compression == CompressionType.SGILOG24)
    //          {
    //            IByteChannelImage image = (IByteChannelImage)_image;
    //            int numSamples = _currBlock.Width;
    //            byte[] red = new byte[numSamples];
    //            byte[] green = new byte[numSamples];
    //            byte[] blue = new byte[numSamples];
    //            LogLuvConversion.convertLogLuv24InterleavedtoRGB24Planar(data, red, green, blue, numSamples);
    //            image.putByteSamples(RGBIndex.Red, getX1() - _codec.BoundsX1 + leftPixels, y, numPixels, 1, red, leftPixels);
    //            image.putByteSamples(RGBIndex.Green, getX1() - _codec.BoundsX1 + leftPixels, y, numPixels, 1, green, leftPixels);
    //            image.putByteSamples(RGBIndex.Blue, getX1() - _codec.BoundsX1 + leftPixels, y, numPixels, 1, blue, leftPixels);
    //          }
    //        break;
    //      }
    //    case (ImageType.LOGL):
    //      {
    //        IByteChannelImage image = (IByteChannelImage)_image;
    //        int numSamples = _currBlock.Width;
    //        byte[] gray = new byte[numSamples];
    //        LogLuvConversion.convertLogL16toGray8(data, gray, numSamples);
    //        image.putByteSamples(0, getX1() - _codec.BoundsX1 + leftPixels, y, numPixels, 1, gray, leftPixels);
    //        break;
    //      }
    //  }
    //}




    //public static abstract Compression CreateCompressionInstance(TiffBitmapPage image);


    #endregion private members

  }

}
